<?php

namespace Gql\Middleware;


use Gql\Contracts\MiddlewareInterface;
use Gql\Contracts\TokenInvalidInterface;
use Gql\Core\Context;
use Gql\Core\DependencyTrait;
use Gql\Errors\JWTError;
use Gql\Errors\NotLoginError;
use Gql\Errors\TokenExpiredError;
use Gql\Errors\TokenInvalidError;
use Gql\Tools\ResolveSchema;
use Illuminate\Container\Container;
use Illuminate\Http\Request;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
use Tymon\JWTAuth\Facades\JWTAuth;

/**
 * 解析比较复杂的对象数据
 * Class GraphResolveMiddleware
 * @package App\PhpGraph\Middleware
 */
class ResolveMiddleware implements MiddlewareInterface
{
    use DependencyTrait;
    protected $resolve;
    protected $container;

    public function __construct(ResolveSchema $resolve, Container $container)
    {
        $this->resolve = $resolve;
        $this->container = $container;
        \Config::set('auth.defaults.guard', 'admin');
    }


    public function handle(Context $ctx, callable $next)
    {
        if (is_null($ctx->body) && (!$ctx->isScalar || $ctx->typeName === 'Mutation')) {
            if ($ctx->fieldName == 'login' && $ctx->typeName == 'Query') {
                $ctx->isPublic = true;
            }
            $hasError = $this->checkToken($ctx);
            if (!$ctx->isIntrospection && $hasError) {
//                return $hasError;
            }
            if (!$ctx->typeName || !$ctx->fieldName) {
                return $next($ctx);
            }
            $className = config('gql.resolve_namespace') . $ctx->typeName;
            $instance = $this->getClassInstance($className);
            $method = $ctx->fieldName;

            if (method_exists($instance, $method)) {
                $parameters = $this->resolveClassMethodDependencies($ctx->toArray(), $className, $ctx->fieldName);
                $ctx->body = $instance->{$method}(...$parameters);
            }
        }
        return $next($ctx);
    }

    protected function getClassInstance(string $className)
    {
        if (!$this->container->has($className)) {
            $this->container->singleton($className, $className);
        }
        return $this->container->get($className);
    }


    public static function generateToken()
    {
        try {
            $requestToken = JWTAuth::getToken();
            if (!JWTAuth::authenticate($requestToken)) {
                return new NotLoginError('请先登录');
            }
        } catch (TokenExpiredException $exception) {
            try {
                //首先获得过期token 接着刷新token 再接着设置token并且验证token合法性
                $token = JWTAuth::parseToken()->refresh();
                JWTAuth::setToken($token);
                JWTAuth::authenticate($token);
                return $token;
            } catch (JWTException $exception) {
                return new TokenExpiredError('登录信息已过期,请重新登录');
            }

        } catch (TokenInvalidException $exception) {
            return new TokenInvalidError('无效登录信息,请重新登录!');
        } catch (JWTException $exception) {
            return new JWTError('无权访问!');
        }
    }

    protected function checkToken(Context $ctx)
    {
        if (!$ctx->isPublic && $ctx->tokenError instanceof TokenInvalidInterface) {
            return $ctx->tokenError;
        }
    }

}